/**
 * Copyright (c) 2015-2016, Javen Zhou  (javenlife@126.com).
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 */

package com.bankLogisticsSys.controller;

import java.util.List;

import com.bankLogisticsSys.model.Expressinfo;
import com.bankLogisticsSys.model.Expressprocess;
import com.bankLogisticsSys.model.Expressrecord;
import com.bankLogisticsSys.model.Packageinfo;
import com.bankLogisticsSys.model.Userinfo;
import com.bankLogisticsSys.service.CreateMsgService;
import com.bankLogisticsSys.service.ExpressService;
import com.bankLogisticsSys.service.LogisticsService;
import com.bankLogisticsSys.service.ManageUserService;
import com.bankLogisticsSys.service.TransformService;
import com.bankLogisticsSys.util.WeixinUtil;
import com.jfinal.log.Log;
import com.jfinal.plugin.activerecord.Db;
import com.jfinal.plugin.redis.Cache;
import com.jfinal.plugin.redis.Redis;
import com.jfinal.weixin.sdk.api.ApiConfig;
import com.jfinal.weixin.sdk.jfinal.MsgControllerAdapter;
import com.jfinal.weixin.sdk.msg.in.InImageMsg;
import com.jfinal.weixin.sdk.msg.in.InLinkMsg;
import com.jfinal.weixin.sdk.msg.in.InLocationMsg;
import com.jfinal.weixin.sdk.msg.in.InShortVideoMsg;
import com.jfinal.weixin.sdk.msg.in.InTextMsg;
import com.jfinal.weixin.sdk.msg.in.InVideoMsg;
import com.jfinal.weixin.sdk.msg.in.InVoiceMsg;
import com.jfinal.weixin.sdk.msg.in.event.InCustomEvent;
import com.jfinal.weixin.sdk.msg.in.event.InFollowEvent;
import com.jfinal.weixin.sdk.msg.in.event.InLocationEvent;
import com.jfinal.weixin.sdk.msg.in.event.InMassEvent;
import com.jfinal.weixin.sdk.msg.in.event.InMenuEvent;
import com.jfinal.weixin.sdk.msg.in.event.InQrCodeEvent;
import com.jfinal.weixin.sdk.msg.in.event.InShakearoundUserShakeEvent;
import com.jfinal.weixin.sdk.msg.in.event.InTemplateMsgEvent;
import com.jfinal.weixin.sdk.msg.in.event.InVerifyFailEvent;
import com.jfinal.weixin.sdk.msg.in.event.InVerifySuccessEvent;
import com.jfinal.weixin.sdk.msg.in.speech_recognition.InSpeechRecognitionResults;
import com.jfinal.weixin.sdk.msg.out.OutNewsMsg;
import com.jfinal.weixin.sdk.msg.out.OutTextMsg;
import com.jfinal.weixin.sdk.msg.out.OutVoiceMsg;

/**
 * 将此 DemoController 在YourJFinalConfig 中注册路由， 并设置好weixin开发者中心的 URL 与 token ，使
 * URL 指向该 DemoController 继承自父类 WeixinController 的 index
 * 方法即可直接运行看效果，在此基础之上修改相关的方法即可进行实际项目开发
 */
public class WeixinMsgController extends MsgControllerAdapter {
	
	
	//get揽收 send发送 receive接收
	public static final int GET_ACTION = 0;
	public static final int SEND_ACTION = 1;
	public static final int RECEIVE_ACTION = 2;
	public static final int PACK_ACTION = 3;
	public static final int ACCEPT_ACTION = 4;
	
	//normal 正常 action_err操作错误 path_err运输路径错误
	public static final int NORMAL = 0;
	public static final int ACTION_ERR = 1;
	public static final int PATH_ERR = 2;
	
	//角色
	public static final int POSTMAN = 5;
	
	//redis的生存时间
	public static final int EXPIRE_TIME = 500;
	
	public static final String TRACKINGID = "trackingID";
	public static final String SEQUENCE = "sequence";
	public static final String ACTION = "action";
	public static final String ABNORMAL = "abnormal";
	
	public static final String NEXT_NETWORK = "nextNetwork";
	
	public static String nearbyContent;// 附近
	public static String location;// 地理位置114.037125,22.645319
	public static String weahterContent;
	public static String openId;
	public String Regex = "[\\+ ~!@#%^-_=]?";
	static Log logger = Log.getLog(WeixinMsgController.class);
	private static final String helpStr = 
			"用户您好,欢迎关注银行物流管理公众号 \n\n\t发送 help 可获取帮助信息\n\n"
			+ "银行和邮政员工请点击\n个人中心->登录 绑定账号" + "\n" 
			+ "\n点击\n个人中心->帮助，显示帮助信息" + "\n"
			+ "发送下方对应的数字获取相应的操作帮助\n"
			+ "1、如何注册和登陆" + "\n" 
			+ "2、如何派单" + "\n" 
			+ "3、如何揽收" + "\n" 
			+ "4、网点接收和发送" + "\n" 
			+ "5、包裹送达与验收" + "\n" 
			+ "6、空包退回" + "\n" 
			+ "7、查询包裹信息" + "\n";

	public ApiConfig getApiConfig() {
		return WeixinUtil.getApiConfig();
	}

	/**
	 * 实现父类抽方法，处理文本消息 本例子中根据消息中的不同文本内容分别做出了不同的响应，同时也是为了测试 jfinal weixin
	 * sdk的基本功能： 本方法仅测试了 OutTextMsg、OutNewsMsg、OutMusicMsg 三种类型的OutMsg，
	 * 其它类型的消息会在随后的方法中进行测试
	 */
	protected void processInTextMsg(InTextMsg inTextMsg) {
		String msgContent = inTextMsg.getContent().trim();
		OutTextMsg outMsg = new OutTextMsg(inTextMsg);
		// 帮助提示
		CreateMsgService mservice = new CreateMsgService();
		outMsg.setContent(mservice.getHelpMsg(msgContent));
//		outMsg.setMsgType("transfer_customer_service");
		render(outMsg);
	}

	/**
	 * 实现父类抽方法，处理图片消息
	 */
	protected void processInImageMsg(InImageMsg inImageMsg) {
		renderOutTextMsg("\t消息已成功接收\n\n" + helpStr);
	}

	/**
	 * 实现父类抽方法，处理语音消息
	 */
	protected void processInVoiceMsg(InVoiceMsg inVoiceMsg) {
		OutVoiceMsg outMsg = new OutVoiceMsg(inVoiceMsg);
		// 将刚发过来的语音再发回去
		outMsg.setMediaId(inVoiceMsg.getMediaId());
		render(outMsg);
	}

	/**
	 * 实现父类抽方法，处理视频消息
	 */
	protected void processInVideoMsg(InVideoMsg inVideoMsg) {
		renderOutTextMsg("\t消息已成功接收\n\n" + helpStr);
	}

	@Override
	protected void processInShortVideoMsg(InShortVideoMsg inShortVideoMsg) {
		OutTextMsg outMsg = new OutTextMsg(inShortVideoMsg);
		outMsg.setContent("\t视频消息已成功接收，该视频的 mediaId 为: " + inShortVideoMsg.getMediaId());
		render(outMsg);
	}

	/**
	 * 实现父类抽方法，处理地址位置消息
	 */
	protected void processInLocationMsg(InLocationMsg inLocationMsg) {
		renderOutTextMsg("\t消息已成功接收\n\n" + helpStr);
	}

	@Override
	protected void processInQrCodeEvent(InQrCodeEvent inQrCodeEvent) {
		if (InQrCodeEvent.EVENT_INQRCODE_SUBSCRIBE.equals(inQrCodeEvent.getEvent())) {
			logger.debug("扫码未关注：" + inQrCodeEvent.getFromUserName());
			OutTextMsg outMsg = new OutTextMsg(inQrCodeEvent);
			outMsg.setContent("感谢您的关注，二维码内容：" + inQrCodeEvent.getEventKey());
			render(outMsg);
		}
		if (InQrCodeEvent.EVENT_INQRCODE_SCAN.equals(inQrCodeEvent.getEvent())) {
			logger.debug("扫码已关注：" + inQrCodeEvent.getFromUserName());
			String key = inQrCodeEvent.getEventKey();
			renderOutTextMsg(key);
		}
	}

	@Override
	protected void processInLocationEvent(InLocationEvent inLocationEvent) {
		logger.debug("发送地理位置事件：" + inLocationEvent.getFromUserName());
		OutTextMsg outMsg = new OutTextMsg(inLocationEvent);
		outMsg.setContent("地理位置是：\n" + inLocationEvent.getLatitude()+"\n"+inLocationEvent.getLongitude());
		render(outMsg);
	}

	@Override
	protected void processInMassEvent(InMassEvent inMassEvent) {
		logger.debug("测试方法：processInMassEvent()");
		renderNull();
	}

	/**
	 * 实现父类抽方法，处理自定义菜单事件
	 */
	
	protected void processInMenuEvent(InMenuEvent inMenuEvent) {
		Cache actionCache = Redis.use();
		logger.debug("菜单事件：" + inMenuEvent.getFromUserName());
		OutTextMsg outMsg = new OutTextMsg(inMenuEvent);
		openId = inMenuEvent.getFromUserName();
		String event = inMenuEvent.getEvent();
		String eventName = inMenuEvent.getEventKey();
		int action;
		long trackingID;
		Boolean flag;
		int sequence;
		LogisticsService service = new LogisticsService();
		ManageUserService userservice = new ManageUserService();
		CreateMsgService msgservice = new CreateMsgService();
		ExpressService exservice = new ExpressService();
		
		Userinfo user = userservice.getUserByOpenid(openId);
		if(user == null){
			outMsg.setContent("请先登录!");
			render(outMsg);
			return;
		}
		/*对click按钮操作的判定
		 * get：		揽收
		 * send：	发送
		 * receive：接收
		 * pack:    封包
		 * accept:  验收
		 * help:	帮助
		*/
		if(event.equals("CLICK")){
			if(eventName.equals("receive") && (user.getRole()==3||user.getRole()==4)){
				outMsg.setContent(msgservice.startProcessMsg(RECEIVE_ACTION));
				action = RECEIVE_ACTION;
				actionCache.hset(openId, ACTION, action);
			}else if(eventName.equals("send")&& (user.getRole()==3||user.getRole()==4)){
				outMsg.setContent(msgservice.startProcessMsg(SEND_ACTION));
				action = SEND_ACTION;
				actionCache.hset(openId, ACTION, action);
			}else if(eventName.equals("get")&& user.getRole()==5){
				outMsg.setContent(msgservice.startProcessMsg(GET_ACTION));
				action = GET_ACTION;
				actionCache.hset(openId, ACTION, action);
			}else if(eventName.equals("pack") && (user.getRole()==1||user.getRole()==2)){
				outMsg.setContent(msgservice.startProcessMsg(PACK_ACTION));	
				action = SEND_ACTION;
				actionCache.hset(openId, ACTION, action);
			}else if(eventName.equals("accept")&& (user.getRole()==1||user.getRole()==2)){
				outMsg.setContent(msgservice.startProcessMsg(ACCEPT_ACTION));	
				action = RECEIVE_ACTION;
				actionCache.hset(openId, ACTION, action);
			}else if(eventName.equals("help")){
				outMsg.setContent(msgservice.helpMsg());	
			}else if(eventName.equals("returnPk")&& user.getRole()==2){
				int expressNum = returnPk(user);
				if(expressNum!=0){
					outMsg.setContent("空包退回，已创建" + expressNum + "个快递单\n"
							+ "请将本行所有的空包根据所属支行分别交由揽投员扫码揽收");
				}else{
					outMsg.setContent("没有未退回的包裹");
				}
			}else{
				outMsg.setContent("错误的操作");
			}
		}else if(event.equals("scancode_waitmsg")){
			/*扫码操作的判定
			 * action_scan:	物流扫码
			 * package_distinguish：	包裹查询
			 * 
			 * flag:标记上行下行 true上行 false:下行
			 * 
			 * sequence:快递传输顺序
			 * bussiness:业务
			 * abnormal:异常标志 0正常 1操作异常 2线路异常
			 */
			if(inMenuEvent.getEventKey().equals("action_scan")){
				actionCache.expire(openId, EXPIRE_TIME);	//设置key的生存时间
				int abnormal = NORMAL;	//异常标志 0正常 1操作异常2路线异常
				
				//判断包裹是否存在
				String scanResult = inMenuEvent.getScanCodeInfo().getScanResult();
				String packageCode = scanResult;
				if(!msgservice.isPackage(packageCode)){
					outMsg.setContent("抱歉，无法找到您扫描的包裹，请确认此包裹是否添加到数据库");
					render(outMsg);
					return;
				}
				Packageinfo pi = exservice.getPackageinfo(packageCode);
								
				//获取快递单号
				Expressrecord er = exservice.getRecord(pi.getId());
				System.out.println(er);
				if(service.getTrackingIDByRecord(er)){
					trackingID = er.getTrackingID();
				}else if(actionCache.hexists(openId, "packTrackingID")){
					trackingID = actionCache.hget(openId, "packTrackingID");
				}else{
					outMsg.setContent("系统中无对应的快递单数据,请点击封包菜单新建快递单，或点击个人中心申报异常");
					pi.setLocation(user.getNetworkID());
					pi.update();
					render(outMsg);
					return;
				}
				
				Expressinfo ei = exservice.getInfoByTrackingID(trackingID);
				//判断多个包裹上下行操作是否一致
				if(actionCache.hexists(openId, "flag")){
					flag = actionCache.hget(openId, "flag");
					if(ei.getFlag() != flag){
						outMsg.setContent("请勿同时操作上下行包裹");
						render(outMsg);
						return;
					}
				}else{
					actionCache.hset(openId, "flag", ei.getFlag());
					flag = ei.getFlag();
				}
				
				//判断是否完善包裹信息，目的地,同时判断是否有发送路线
				if(!service.setDestination(trackingID, pi)){
					outMsg.setContent("该分行站点的包裹尚未配置发送路线，请联系管理员配置发送路线");
					render(outMsg);
					return;
				}
				ei = exservice.getInfoByTrackingID(trackingID);
				
				//获取sequence action站点顺序和操作
				Expressprocess ep = exservice.getProcess(trackingID);
				System.out.println(trackingID);
				if(ep!=null){
					if(user.getRole()==5){
						action = GET_ACTION;
						sequence = ep.getSequence();
					}else if(user.getRole()== 1 || user.getRole()==2){
						action = RECEIVE_ACTION;
						sequence = ep.getSequence()+ 1;
					}else if(user.getNetworkID()==ep.getNetworkID() && ep.getAction()!=0){
						action = SEND_ACTION;
						sequence = ep.getSequence();
					}else{
						action = RECEIVE_ACTION;
						sequence = ep.getSequence()+ 1;
					}
					if(actionCache.hget(openId, ACTION)!=null){
						if((int)actionCache.hget(openId, ACTION)!=action){
							abnormal = ACTION_ERR;
							action = actionCache.hget(openId, ACTION);
						}
					}
				}else{
					action = SEND_ACTION;
					sequence = 1;
				}
				
				//获取下一站的id
				int nextNetwork = service.getNextNetwork(ei, sequence);
				System.out.println("nextNetwork" + nextNetwork);
				if(!actionCache.hexists(openId, NEXT_NETWORK)){
					actionCache.hset(openId, NEXT_NETWORK, nextNetwork);
				}
				//判断运输路线是否错误
				if(service.isPathError(ei, user, sequence, flag)){
					abnormal = PATH_ERR;
					if(user.getBank()!=1 && user.getNetworkID() != ei.getDestination()){
						outMsg.setContent("此包裹不应由当前银行验收，请交由揽投员发往正确的站点");
						render(outMsg);
						return;
					}
					int temp;
					if(flag){
						temp = ei.getSource();
					}else{
						temp = ei.getDestination();
					}
					sequence = TransformService.getSequence(user.getNetworkID(), temp, pi.getBusinessID());
				}
				
				//判断多个包裹的操作是否一致，不允许同时进行多种操作
				if(actionCache.hexists(openId, ACTION)){
					if((int)actionCache.hget(openId, ACTION)== action){
						if(action == SEND_ACTION && nextNetwork!=(int)actionCache.hget(openId, NEXT_NETWORK)){
							outMsg.setContent("发往不同站点的包裹不能同时发送");
							render(outMsg);
							return;
						}
					}else{
						outMsg.setContent("请勿同时进行收发操作");
						render(outMsg);
						return;
					}
				}else{
					actionCache.hset(openId, ACTION, action);
				}
				actionCache.sadd(openId+"List", String.valueOf(trackingID));
				actionCache.hset(String.valueOf(trackingID), SEQUENCE, sequence);
				actionCache.hset(String.valueOf(trackingID), ABNORMAL, abnormal);
				actionCache.expire(openId+"List", EXPIRE_TIME);
				//判断是否重复扫码，将数据入库
				if(service.isRepeatScan(trackingID, user, pi.getId(), action)){
					outMsg.setContent("请勿重复扫码");
				}else{
					dealScanAction(trackingID, pi.getId(), action, user, ei);
					outMsg.setContent(msgservice.getActionMsg2(trackingID, sequence, action, user, abnormal));
				}
			}else{
				//包裹识别
				String scanResult = inMenuEvent.getScanCodeInfo().getScanResult();
				outMsg.setContent(msgservice.isPackageMsg(scanResult));
			}
		}else{
			outMsg.setContent("未找到当前菜单：" + inMenuEvent.getEventKey());
		}
		render(outMsg);
	}

	@Override
	protected void processInSpeechRecognitionResults(InSpeechRecognitionResults inSpeechRecognitionResults) {
		logger.debug("语音识别事件：" + inSpeechRecognitionResults.getFromUserName());
		OutTextMsg outMsg = new OutTextMsg(inSpeechRecognitionResults);
		outMsg.setContent("语音识别内容是：" + inSpeechRecognitionResults.getRecognition());
		render(outMsg);
	}

	/**
	 * 实现父类抽方法，处理链接消息 特别注意：测试时需要发送我的收藏中的曾经收藏过的图文消息，直接发送链接地址会当做文本消息来发送
	 */
	protected void processInLinkMsg(InLinkMsg inLinkMsg) {
		OutNewsMsg outMsg = new OutNewsMsg(inLinkMsg);
		outMsg.addNews("链接消息已成功接收", "链接使用图文消息的方式发回给你，还可以使用文本方式发回。点击图文消息可跳转到链接地址页面，是不是很好玩 :)",
				"http://mmbiz.qpic.cn/mmbiz/zz3Q6WSrzq1ibBkhSA1BibMuMxLuHIvUfiaGsK7CC4kIzeh178IYSHbYQ5eg9tVxgEcbegAu22Qhwgl5IhZFWWXUw/0",
				inLinkMsg.getUrl());
		render(outMsg);
	}

	@Override
	protected void processInCustomEvent(InCustomEvent inCustomEvent) {
		System.out.println("processInCustomEvent() 方法测试成功");
	}

	/**
	 * 实现父类抽方法，处理关注/取消关注消息
	 */
	protected void processInFollowEvent(InFollowEvent inFollowEvent) {
		OutTextMsg outMsg = new OutTextMsg(inFollowEvent);
		outMsg.setContent(helpStr);
		// 如果为取消关注事件，将无法接收到传回的信息
		render(outMsg);
	}

	// 处理接收到的模板消息是否送达成功通知事件
	protected void processInTemplateMsgEvent(InTemplateMsgEvent inTemplateMsgEvent) {
		String status = inTemplateMsgEvent.getStatus();
		renderOutTextMsg("模板消息是否接收成功：" + status);
	}

	@Override
	protected void processInShakearoundUserShakeEvent(InShakearoundUserShakeEvent inShakearoundUserShakeEvent) {
		logger.debug("摇一摇周边设备信息通知事件：" + inShakearoundUserShakeEvent.getFromUserName());
		OutTextMsg outMsg = new OutTextMsg(inShakearoundUserShakeEvent);
		outMsg.setContent("摇一摇周边设备信息通知事件UUID：" + inShakearoundUserShakeEvent.getUuid());
		render(outMsg);
	}

	@Override
	protected void processInVerifySuccessEvent(InVerifySuccessEvent inVerifySuccessEvent) {
		logger.debug("资质认证成功通知事件：" + inVerifySuccessEvent.getFromUserName());
		OutTextMsg outMsg = new OutTextMsg(inVerifySuccessEvent);
		outMsg.setContent("资质认证成功通知事件：" + inVerifySuccessEvent.getExpiredTime());
		render(outMsg);
	}

	@Override
	protected void processInVerifyFailEvent(InVerifyFailEvent inVerifyFailEvent) {
		logger.debug("资质认证失败通知事件：" + inVerifyFailEvent.getFromUserName());
		OutTextMsg outMsg = new OutTextMsg(inVerifyFailEvent);
		outMsg.setContent("资质认证失败通知事件：" + inVerifyFailEvent.getFailReason());
		render(outMsg);
	}
	
	public int returnPk(Userinfo user){
		String sql = "select * from packageinfo where location = ?";
		String sql2 = "select id from packageinfo where location = ? and networkid = ? and businessID = ? and state = 0";
		long trackingID;
		LogisticsService ls = new LogisticsService();
		List<Packageinfo> packageList = Packageinfo.dao.find(sql, user.getNetworkID());
		int expressNum = packageList.size();
		for(int i=0;i<expressNum;i++){
//			List<Integer> packageList = Db.query(sql2, user.getNetworkID(),networkList.get(i));
			trackingID = ls.createExpress2(user,i,packageList.get(i).getNetworkID(),packageList.get(i).getBusinessID());
			Expressinfo ei = Expressinfo.dao.findFirst("select * from expressinfo where trackingid = ?",trackingID);
			ls.addPackage(trackingID,packageList.get(i).getId());
			ls.addRecord(trackingID, packageList.get(i).getId(), SEND_ACTION, user);
			ls.addProcess(trackingID, 1, 0, SEND_ACTION, user);
		}
		return expressNum;
	}
	public void dealScanAction(long trackingID, int packageid, int action ,Userinfo user, Expressinfo ei){
		LogisticsService service = new LogisticsService();
		Packageinfo pi = Packageinfo.dao.findById(packageid);
		service.addRecord(trackingID, packageid, action, user);
		if(user.getNetworkID().equals(ei.getSource())){
			service.addPackage(trackingID, packageid);
		}else if(user.getNetworkID().equals(ei.getDestination())){
			pi.setState(0);
		}
		pi.update();
	}
}
